package com.hsgene.pay.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hsgene.common.ClientType;
import com.hsgene.common.util.basic.JsonUtils;
import com.hsgene.common.util.basic.UUID32;
import com.hsgene.common.util.character.CharactersConstant;
import com.hsgene.common.util.exception.ErrorCode;
import com.hsgene.common.util.http.HttpConnectionManager;
import com.hsgene.common.util.result.ApiResult;
import com.hsgene.common.util.result.NullObject;
import com.hsgene.common.util.sign.PayDigestUtil;
import com.hsgene.order.constants.PayType;
import com.hsgene.order.constants.PayerType;
import com.hsgene.order.constants.v1.OrderChargeStatus;
import com.hsgene.order.constants.v1.PaymentBusinessType;
import com.hsgene.order.constants.v1.PaymentIntStatus;
import com.hsgene.order.domain.v1.GeneticTestingOrderAddtionA;
import com.hsgene.order.domain.v1.GeneticTestingOrderInterchange;
import com.hsgene.order.domain.v1.GeneticTestingOrderPayment;
import com.hsgene.order.exception.OrderErrorCode;
import com.hsgene.pay.domain.CallBackPayType;
import com.hsgene.pay.domain.PayMethod;
import com.hsgene.pay.exception.PayErrorCode;
import com.hsgene.pay.persistence.OrderPayRepository;
import com.hsgene.pay.persistence.OrderPaymentRepository;
import com.hsgene.pay.service.OrderPayService;
import com.hsgene.pay.service.api.OrderInternalApiService;
import com.hsgene.pay.service.api.TripartiteService;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 支付业务接口
 *
 * @author wxf
 * @date 2018/8/13 13:54
 **/
@Service
public class OrderPayServiceImpl implements OrderPayService {

    private static final String SUCCESS = "success";
    private static final String FAIL = "fail";

    /**
     * LOG
     */
    private static Logger LOGGER = LogManager.getLogger(OrderPayServiceImpl.class);
    // 商户ID
    @Value("${pay.mchId}")
    private String mchId;
    // 加签key
    @Value("${pay.reqKey}")
    private String reqKey;
    // 验签key
    @Value("${pay.repKey}")
    private String repKey;
    //支付地址
    @Value("${pay.baseUrl}")
    private String baseUrl;
    //回调地址
    @Value("${pay.notifyUrl}")
    private String notifyUrl;
    /**
     * 用户地址,微信H5支付时要真实的
     */
    @Value("${pay.wxClientIp}")
    private String wxClientIp;

    /**
     * 支付是否执行回调
     */
    @Value("${pay.executeNotify}")
    private String executeNotify;

    /**
     * 设备
     */
    @Value("${pay.device}")
    private String device;

    /**
     * 华生基因支付测试
     */
    @Value("${pay.subject}")
    private String subject;

    /**
     * 华生基因支付测试说明
     */
    @Value("${pay.body}")
    private String body;

    @Value("${order.status}")
    private String orderStatus;
    @Resource
    private OrderPayRepository orderPayRepository;

    @Resource
    private TripartiteService tripartiteService;

    @Resource
    private OrderPaymentRepository orderPaymentRepository;

    @Resource
    private OrderInternalApiService orderInternalApiService;

    /**
     * 订单支付
     *
     * @param orderId   订单id
     * @param payType   支付类型
     * @param payAmount 支付金额
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 3600, rollbackFor =
            Exception.class)
    public ApiResult<Map<String, Object>> orderPay(String orderId, String payType, int payAmount, PaymentBusinessType type) {
        switch (type) {
            case GENETIC_TESTING:
                return payGeneticTesting(orderId, payType, payAmount);
            case GENETIC_TESTING_ADDITION:
                return payGeneticTestingAddition(orderId, payType, payAmount);
            case UNDEFINED:
            default:
                throw new UnsupportedOperationException("不支持的支付业务类型：" + type.getCode());
        }
    }

    private ApiResult<Map<String, Object>> payGeneticTestingAddition(String interchangeId, String payType, int payAmount) {
        ApiResult<GeneticTestingOrderInterchange> interchangeResult = orderInternalApiService.getTestingOrderInterchange(interchangeId);
        if (!interchangeResult.isSucc()) {
            return ApiResult.fail(interchangeResult.getError());
        }
        GeneticTestingOrderInterchange interchange = interchangeResult.getData();
        if (interchange == null || interchange.getDeleteFlag() == 1) {
            return ApiResult.fail(OrderErrorCode.ORDER_INTERCHANGE_NOT_EXISTED);
        }
        /*if (!Integer.valueOf(1).equals(interchange.getReplenishmentFlag()) || Integer.valueOf(2).equals(interchange.getStatus())) {
            return ApiResult.fail(OrderErrorCode.ORDER_INTERCHANGE_REPAID_NO_NEED);
        }*/
        ApiResult<GeneticTestingOrderAddtionA> addtionAResult = orderInternalApiService.getTestingOrderAdditionA(interchange.getNewOrderId());
        if (!addtionAResult.isSucc()) {
            return ApiResult.fail(addtionAResult.getError());
        }
        GeneticTestingOrderAddtionA addtionA = addtionAResult.getData();
        if (addtionA == null
                || Integer.valueOf(0).equals(addtionA.getIsReplenishment())
                || addtionA.getReplenishmentAmount() == null) {
            return ApiResult.fail(OrderErrorCode.ORDER_INTERCHANGE_REPAID_NO_NEED);
        }
        if (addtionA.getReplenishmentAmount().multiply(BigDecimal.valueOf(100)).intValue() != payAmount) {
            return ApiResult.fail(OrderErrorCode.ORDER_INTERCHANGE_AMOUNT_EXCEPTION);
        }

        PayType payTypeEnum = PayType.getEnum(payType);
        if (interchange.getPayerType() != null) {
            PayerType payerType = PayerType.getEnum(interchange.getPayerType());
            // 当套餐变更处理时选择的是患者支付，则更改支付方式选择为二维码扫码支付
            if (PayerType.PATIENT.equals(payerType)) {
                payTypeEnum = PayType.CHINAUMS_QR;
            }
        }

        // 封装数据,获取参数对象
        JSONObject paramMap = getJsonParamsObject(interchangeId, payTypeEnum.getCode(), payAmount);
        // 发送请求
        String result = getUrlQueryResult(paramMap, baseUrl + "/pay/create_order");
        Map<String, Object> resultMap = getPostResultInfo(result);
        if (CollectionUtils.isEmpty(resultMap) || !"SUCCESS".equals(resultMap.get("retCode"))) {
            LOGGER.error("支付请求失败：" + JsonUtils.objectToJson(resultMap));
            return ApiResult.fail(PayErrorCode.PAYMENT_FAILED);
        }
        GeneticTestingOrderPayment payment = buildGeneticTestingOrderPayment(interchange, addtionA, payTypeEnum, resultMap);
        orderPaymentRepository.save(payment);
        return ApiResult.succ(resultMap);
    }

    private GeneticTestingOrderPayment buildGeneticTestingOrderPayment(
            GeneticTestingOrderInterchange interchange,
            GeneticTestingOrderAddtionA addtionA,
            PayType payType, Map<String, Object> resultMap) {
        GeneticTestingOrderPayment orderPayment = new GeneticTestingOrderPayment();

        orderPayment.setId(UUID32.randomUUIDString());
        orderPayment.setOrderId(interchange.getNewOrderId());
        orderPayment.setBusinessId(interchange.getId());
        orderPayment.setBusinessType(PaymentBusinessType.GENETIC_TESTING_ADDITION.getCode());
        orderPayment.setPaymentNo(String.valueOf(resultMap.get("payOrderId")));
        orderPayment.setPaymentWay(payType.getValue());
        orderPayment.setPaymentAmount(addtionA.getReplenishmentAmount());
        orderPayment.setPaymentStatus(PaymentIntStatus.ON_GOING.getCode());
        orderPayment.setUpdateDateTime(new Date());

        return orderPayment;
    }

    private ApiResult<Map<String, Object>> payGeneticTesting(String orderId, String payType, int payAmount) {
        ErrorCode errorCode = checkPayInfo(orderId, payType, payAmount);
        if (errorCode != null) {
            return ApiResult.fail(errorCode);
        }
        Date date = new Date();
        //封装数据,获取参数对象
        JSONObject paramMap = getJsonParamsObject(orderId, payType, payAmount);
        //发送请求
        String result = getUrlQueryResult(paramMap, baseUrl + "/pay/create_order");
        Map<String, Object> resultMap = getPostResultInfo(result);
        //微信支付信息单独插入genetic_testing_order_payment表
        Integer chanel = null;
        switch (payType) {
            //微信支付
            case "WX_APP":
                chanel = 2;
                break;
            //银联扫码
            case "CHINAUMS_QR":
                chanel = 3;
                break;
            default:
                break;
        }
        if (chanel != null) {
            inserOrderPaymentInfo(orderId, payAmount, date, resultMap, chanel, PayType.getEnum(payType));
        }
        return ApiResult.succ(resultMap);
    }

    private ErrorCode checkPayInfo(String orderId, String payType, Integer payAmount) {
        if (StringUtils.isEmpty(orderId)) {
            return OrderErrorCode.ORDER_ID_IS_NOT_NULL;
        }
        //判断支付金额和订单是否一致
        Double amount = checkOrderAmount(orderId);
        if (amount == null) {
            LOGGER.error("params payAmount：" + payAmount + ",order payAmount：" + amount * 100);
            return PayErrorCode.ORDER_PAY_AMOUNT_ERROR;
        }
        BigDecimal bd = new BigDecimal(amount * 100).setScale(0, BigDecimal.ROUND_HALF_UP);
        if (bd.intValue() != payAmount) {
            LOGGER.error("params payAmount：" + payAmount + ",order payAmount：" + amount * 100);
            return PayErrorCode.ORDER_PAY_AMOUNT_ERROR;
        }
        if (StringUtils.isEmpty(payType)) {
            LOGGER.error("pay type is null");
            return PayErrorCode.PAY_TYPE_IS_NOT_NULL;
        }
        return null;
    }

    private void inserOrderPaymentInfo(String orderId, int payAmount, Date date, Map<String, Object> resultMap, int
            channel, PayType payType) {
        //删除存在的且未支付的数据
        orderPayRepository.deleteNoPayInfo(orderId);
        GeneticTestingOrderPayment orderPayment = new GeneticTestingOrderPayment();
        orderPayment.setOrderId(orderId);
        orderPayment.setPaymentWay(payType.getValue());
        //微信支付
        orderPayment.setPaymentChannel(channel);
        //支付状态变成支付中
        orderPayment.setPaymentStatus(1);
        orderPayment.setUpdateDateTime(date);
        orderPayment.setPaymentNo(String.valueOf(resultMap.get("payOrderId")));
        double amount = payAmount / 100.0;
        orderPayment.setPaymentAmount(BigDecimal.valueOf(amount));
        orderPayRepository.insertPayInfo(orderPayment);
    }

    /**
     * @param
     * @description 获取支付方式列表
     * @author maodi
     * @createDate 2018/9/30 11:11
     */
    @Override
    public List<PayMethod> queryPayMethodList() {
        return orderPayRepository.getPayMethodList();
    }

    /**
     * 支付回调
     *
     * @param paramMap 参数
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 3600, rollbackFor =
            Exception.class)
    public String payCallback(Map<String, Object> paramMap) {
        //支付单号
        String payOrderId = (String) paramMap.get("payOrderId");
        String result = null;
        GeneticTestingOrderPayment payment = orderPaymentRepository.findByPaymentNo(payOrderId);
        //TODO 代码处理太乱，爱谁改谁改
        if (payment == null || PaymentBusinessType.GENETIC_TESTING.equals(PaymentBusinessType.getEnum(payment
                .getBusinessType()))) {
            result = payOrderCallBack(paramMap, payOrderId);
        } else if (PaymentBusinessType.GENETIC_TESTING_ADDITION.equals(PaymentBusinessType.getEnum(payment
                .getBusinessType()))) {
            result = payInterchangeCallBack(paramMap, payment);
        } else {
            result = SUCCESS;
            LOGGER.error("无对应的支付业务类型，回调请求参数json：{}", JsonUtils.objectToJson(paramMap));
        }
        return result;
    }

    // 补寄补款支付回调处理
    private String payInterchangeCallBack(Map<String, Object> paramMap, GeneticTestingOrderPayment payment) {
        // 变更记录id
        String interchangeId = (String) paramMap.get("mchOrderNo");
        BigDecimal amount = new BigDecimal((String) paramMap.get("amount")).divide(new BigDecimal("100"), 2,
                RoundingMode.HALF_UP);
        ApiResult<NullObject> callbackResult = orderInternalApiService.payInterchangeCallBack(interchangeId, amount);
        if (!callbackResult.isSucc()) {
            LOGGER.error("订单套餐变更补款支付回调失败：", callbackResult.getDesc());
            return FAIL;
        }
        //更新支付表为已付款
        if (!amount.equals(payment.getPaymentAmount())) {
            LOGGER.error("支付回调金额[{}]与记录金额[{}]不一致，json={}", amount, payment.getPaymentAmount(), JsonUtils.objectToJson
                    (payment));
            payment.setPaymentAmount(amount);
        }
        payment.setPaymentStatus(2);
        Date paymentDateTime = new Date(Long.valueOf(String.valueOf(paramMap.get("paySuccTime"))));
        payment.setPaymentDateTime(paymentDateTime);
        payment.setPaymentChannel(getPayType(paramMap));
        orderPayRepository.update(payment);
        return SUCCESS;
    }

    // 正常订单支付回调处理
    private String payOrderCallBack(Map<String, Object> paramMap, String payOrderId) {
        //订单ID
        String mchOrderNo = (String) paramMap.get("mchOrderNo");
        //获取支付方式
        Integer payTypeValue = getPayType(paramMap);
        //支付回调执行，说明订单支付完成，已生成正式订单，则数据推送给机构,异步执行
        //sendAgencyOrderInfo(mchOrderNo);

        if (!verifyPayResponse(paramMap)) {
            String errorMessage = "verify request param failed.";
            LOGGER.warn(errorMessage);
            return "fail";
        }
        Integer status = orderPayRepository.getGoodsOrder(mchOrderNo);
        if (status == null) {
            return "fail";
        }
        String[] array = orderStatus.split(CharactersConstant.COMMA_EN);
        for (String str : array) {
            if (str.equals(String.valueOf(status))) {
                LOGGER.info("已支付订单状态不可再更新,payOrderId={},mchOrderNo={}", payOrderId, mchOrderNo);
                return "success";
            }
        }
        Date date = new Date(Long.valueOf(String.valueOf(paramMap.get("paySuccTime"))));
        // 更新订单表
        int ret = orderPayRepository.updateStatus4Success(mchOrderNo, date, OrderChargeStatus.CHARGED.getValue());
        //更新订单套餐表
        orderPayRepository.updateOrderPackage(mchOrderNo, date);
        //更新支付表为已付款
        orderPayRepository.updatePayment(mchOrderNo, payOrderId, Double.parseDouble(String.valueOf(paramMap.get
                ("amount"))) / 100, date, payTypeValue);
        // ret返回结果
        // 等于1表示处理成功,返回支付中心success
        // 其他值,返回支付中心fail,让稍后再通知
        String resStr = ret == 1 ? "success" : "fail";
        updateFirstSingleAward(mchOrderNo);
        LOGGER.info("支付回调处理结果结果:{},payOrderId={},mchOrderNo={}", resStr, payOrderId, mchOrderNo);
        return resStr;
    }

    private Integer getPayType(Map<String, Object> paramMap) {
        //获取支付方式
        String payType = (String) paramMap.get("subChannelType");
        return StringUtils.isEmpty(payType) ? 0 : CallBackPayType.getCode(payType);
    }

    private void updateFirstSingleAward(String mchOrderNo) {
        try {
            //首单奖励处理
            Integer firstSingleAward = 0;
            String patientName = orderPayRepository.queryPatientNameByOrderId(mchOrderNo);
            Integer payerType = orderPayRepository.queryPayerTypeByOrderId(mchOrderNo);
            Integer orderOrigin = orderPayRepository.queryOrderOriginByOrderId(mchOrderNo);
            if (orderOrigin != null && (ClientType.PEANUT.getCode().equals(orderOrigin) || ClientType
                    .GOLD_COLORED_GLASS.getCode().equals(orderOrigin)) && StringUtils.isNotBlank(patientName)) {
                Integer orderCount = orderPayRepository.queryOrderCountByPatientName(patientName);
                if (orderCount != null && orderCount == 1 && payerType != null && payerType == 0) {
                    firstSingleAward = 1;
                }
            }
            orderPayRepository.updateFirstSingleAward(new Date(), firstSingleAward, mchOrderNo);
        } catch (Exception e) {
            LOGGER.error("首单奖励处理出错", e);
        }
    }


    /**
     * 获取金额
     *
     * @param orderId 订单id
     * @return
     */
    @Override
    public Double checkOrderAmount(String orderId) {
        return orderPayRepository.getGoodsOrderMoney(orderId);
    }

    @Async
    protected void sendAgencyOrderInfo(String orderId) {
        tripartiteService.sendAgencyOrderInfo(orderId);
    }

    /**
     * 查询支付订单
     *
     * @param orderId    订单id
     * @param payOrderId 支付id
     * @return
     */
    @Override
    public Map<String, Object> queryPayOrder(String orderId, String payOrderId) {
        JSONObject paramMap = new JSONObject();
        // 商户ID
        paramMap.put("mchId", mchId);
        // 商户订单号
        paramMap.put("mchOrderNo", orderId);
        // 支付订单号
        paramMap.put("payOrderId", payOrderId);
        // 是否执行回调,true或false,如果为true当订单状态为支付成功(2)时,
        // 支付中心会再次回调一次业务系统
        paramMap.put("executeNotify", executeNotify);
        //签名
        String reqSign = PayDigestUtil.getSign(paramMap, reqKey);
        paramMap.put("sign", reqSign);
        String result = getUrlQueryResult(paramMap, baseUrl + "/pay/query_order");
        return getPostResultInfo(result);
    }

    private JSONObject getJsonParamsObject(String orderId, String payType, int payAmount) {
        JSONObject paramMap = new JSONObject();
        // 商户ID
        paramMap.put("mchId", mchId);
        // 商户订单号
        paramMap.put("mchOrderNo", orderId);
        // 支付渠道ID,JSBANK_QR(江苏银行扫码付), WX_NATIVE(微信扫码),WX_JSAPI(微信公众号或微信小程序),WX_APP(微信APP),WX_MWEB(微信H5),ALIPAY_WAP
        // (支付宝手机支付),ALIPAY_PC(支付宝网站支付),ALIPAY_MOBILE(支付宝移动支付)
        if (PayType.getEnum(payType.toUpperCase()) != null) {
            paramMap.put("channelId", payType.toUpperCase());
        }
        // 支付金额,单位分
        paramMap.put("amount", payAmount);
        // 币种, cny-人民币
        paramMap.put("currency", "cny");
        // 用户地址,微信H5支付时要真实的
        paramMap.put("clientIp", wxClientIp);
        // 设备
        paramMap.put("device", device);
        paramMap.put("subject", subject);
        paramMap.put("body", body);
        // 回调URL
        paramMap.put("notifyUrl", notifyUrl);
        //签名
        String reqSign = PayDigestUtil.getSign(paramMap, reqKey);
        paramMap.put("sign", reqSign);
        return paramMap;
    }

    private String getUrlQueryResult(JSONObject paramMap, String url) {
        LOGGER.info("start request pay info time :" + new Date());
        HttpPost post = new HttpPost(url);
        String result = null;
        try {
            String params = "params=" + paramMap.toJSONString();
            ByteArrayEntity entity = new ByteArrayEntity(params.getBytes("utf-8"));
            entity.setContentType("application/x-www-form-urlencoded");
            post.setEntity(entity);
            CloseableHttpClient httpClient = HttpConnectionManager.getHttpClient();
            CloseableHttpResponse response = httpClient.execute(post);
            result = EntityUtils.toString(response.getEntity(), "utf-8");
            LOGGER.info("end request pay info time :" + new Date());
        } catch (Exception e) {
            LOGGER.error("pay order is error", e);
        }
        return result;
    }

    private Map<String, Object> getPostResultInfo(String result) {
        Map retMap = JSON.parseObject(result);
        if (retMap == null) {
            return null;
        }
        if ("SUCCESS".equals(retMap.get("retCode")) && "SUCCESS".equalsIgnoreCase(retMap.get("resCode").toString())) {
            // 验签
            String checkSign = PayDigestUtil.getSign(retMap, repKey, "sign", "payParams");
            String retSign = (String) retMap.get("sign");
            if (checkSign.equals(retSign)) {
                LOGGER.info("get order is success...");
            } else {
                LOGGER.error("get order is error...");
                return null;
            }
        }
        return retMap;
    }

    private boolean verifyPayResponse(Map<String, Object> map) {
        String mchId = (String) map.get("mchId");
        String payOrderId = (String) map.get("payOrderId");
        String mchOrderNo = (String) map.get("mchOrderNo");
        String amount = (String) map.get("amount");
        String sign = (String) map.get("sign");

        if (StringUtils.isEmpty(mchId)) {
            LOGGER.warn("Params error. mchId={}", mchId);
            return false;
        }
        if (StringUtils.isEmpty(payOrderId)) {
            LOGGER.warn("Params error. payOrderId={}", payOrderId);
            return false;
        }
        if (StringUtils.isEmpty(amount) || !NumberUtils.isNumber(amount)) {
            LOGGER.warn("Params error. amount={}", amount);
            return false;
        }
        if (StringUtils.isEmpty(sign)) {
            LOGGER.warn("Params error. sign={}", sign);
            return false;
        }

        // 验证签名
        /*if (!verifySign(map)) {
            LOGGER.warn("verify params sign failed. payOrderId={}", payOrderId);
            return false;
        }*/
        // 根据payOrderId查询业务订单,验证订单是否存在
        Integer status = orderPayRepository.getGoodsOrder(mchOrderNo);
        if (status == null) {
            LOGGER.warn("业务订单不存在,payOrderId={},mchOrderNo={}", payOrderId, mchOrderNo);
            return false;
        }
        // 核对金额
        Double orderAmount = orderPayRepository.getGoodsOrderMoney(mchOrderNo);
        if (orderAmount * 100 != Long.parseLong(amount)) {
            LOGGER.warn("支付金额不一致,dbPayPrice={},payPrice={}", orderAmount, amount);
            return false;
        }
        return true;
    }

    private boolean verifySign(Map<String, Object> map) {
        String mchId2 = (String) map.get("mchId");
        if (!mchId.equals(mchId2)) {
            LOGGER.warn("商户不一致,mchId={}", mchId2);
            return false;
        }
        LOGGER.info("验签参数map={}", map);
        String localSign = PayDigestUtil.getSign(map, repKey, "sign");
        String sign = (String) map.get("sign");
        LOGGER.info("localSign={}", localSign);
        return localSign.equalsIgnoreCase(sign);
    }
}
